
import re
import datetime

EMAIL_RE = re.compile('[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}', re.IGNORECASE)

def isempty(value):
    '''Returns true if value is None or empty string.'''
    if value == None or (isinstance(value, basestring) and len(value) == 0):
        return True
    
    return False

class Validators:
    
    @staticmethod
    def required(rule, data):
        '''Requires that the specified field has a value.'''
        field = rule[1]
        msg = rule[2]
        
        #field must be supplied
        if field not in data:
            return (field, msg)
        
        #field value cannot equal none
        if data[field] == None:
            return (field, msg)
        
        #field value cannot be an empty string
        if isinstance(data[field], basestring) and len(data[field].strip()) == 0:
            return (field, msg)
        
        return None
    
    @staticmethod
    def float(rule, data):
        '''Requires that the data in field is either empty or can be cast to a float.'''
        field = rule[1]
        msg = rule[2]
        
        if field not in data:
            return None
        
        if isempty(data[field]):
            return None
        
        try:
            val = float(data[field])
            return val
        except ValueError, TypeError:
            return (field, msg)
        
    @staticmethod
    def integer(rule, data):
        '''Requires that the data in field is either empty or can be cast to a float.'''
        field = rule[1]
        msg = rule[2]
        
        if field not in data:
            return None
        
        if isempty(data[field]):
            return None
        
        if isinstance(data[field], float):
            val = int(data[field])
            if val == data[field]:
                return val
            else: 
                return (field, msg)
        
        try:
            val = int(data[field])
            return val
        except ValueError, TypeError:
            return (field, msg)
    
    @staticmethod
    def date(rule, data):
        '''Validates that we were given a valid date.'''
        field = rule[1]
        msg = rule[2]
        
        if field not in data:
            return None
        
        if isempty(data[field]):
            return None
        
        if isinstance(data[field], (datetime.datetime, datetime.date)):
            return data[field]
        
        try:
            t_date = datetime.datetime.strptime(data[field], '%m/%d/%Y').date()
            return t_date
        except ValueError:
            return (field, msg)
        
    
    @staticmethod
    def email(rule, data):
        '''Validates that we were given a valid email address.
        
        Note: does not require the email address to be given.  Just validates 
        it if it was provided.
        '''
        field = rule[1]
        msg = rule[2]
        
        if field not in data:
            return None
        
        if isempty(data[field]):
            return None
        
        if EMAIL_RE.match(data[field]):
            return data[field]
        else:
            return (field, msg)
    
def validate(rules, data):
    '''Validates the data using the specified rules.
    
    rules - list of rules where a rule is a list.
    data - field to value mappings (dict)
    '''
    errors = {}
    
    for rule in rules:
        requirement = rule[0]
        
        if hasattr(Validators, requirement):
            result = getattr(Validators, requirement)(rule, data)
      
            if isinstance(result, tuple):
                errors[result[0]] = result[1]
            elif result != None:
                data[rule[1]] = result
            
        else:
            #flag a warning.
            pass
  
    return errors
    